package com.lime49.lockcrypt;
import java.awt.datatransfer.*;
import java.awt.datatransfer.Transferable;
import java.util.*;
import javax.swing.tree.*;
import javax.swing.event.*;
import java.io.*;
import org.json.*;

public class Group extends AccountTreeNode implements Transferable, Serializable {
    public boolean isGroup = false;
    public static DataFlavor META_FLAVOR = new DataFlavor(Account.class, "Group");
    private static DataFlavor[] flavors = { META_FLAVOR };
    protected Comparator<AccountTreeNode> comparator;
    protected EventListenerList listenerList = new EventListenerList();
    
    /** 
    * Creates a dummy group for use the the 'new group' dialog box
    */
    public Group() {
        super("","");
    }
    
    /**
    * Creates a new group with specified name and parent
    * @param name The name of this group
    * @param parent The parent group
    * @param isChanged Whether or not the group has been changed since the database was loaded
    */
    public Group(String name, String parent, boolean isChanged) {
        super(name, parent, isChanged);
        if(children == null) {
            children = new Vector<Group>();
        }
    }
    
    /**
    * Creates a new group with specified name, parent, created and modified dates
    * @param name The name of this group
    * @param parent The parent group
    * @param isChanged Whether or not the group has been changed since the database was loaded
    * @param created The creation date
    * @param modified The lastmod date
    */
    public Group(String name, String parent, boolean isChanged, long created, long modified) {
        super(name, parent, isChanged, created, modified);
        if(children == null) {
            children = new Vector();
        }
    }
    
    /**
    * Returns a text representation of this group for file writing
    * @return A string representing this group
    */
    public String getOutput() {
        try {
            JSONObject jso = new JSONObject();
            jso.put("Name", getName());
            jso.put("Parent", getParentName());
            jso.put("Created", Long.toString(getCreated()));
            jso.put("Modified", Long.toString(getModified()));
            String jsonString = "grp"+jso.toString();
            return jsonString;
        } catch(Exception ex) {
            return "";
        }
    }
    
    @Override
    /**
    * This group's name
    * @return The name of this group
    */
    public String toString() {
        return super.getName();
    }
    
    @Override
    public boolean getAllowsChildren() { 
        return true; 
    }
    
    @Override
    /**
    * Always returns false
    * @return false
    */
    public boolean isLeaf() {
        return false; 
    }
    
    // ---------- Transferable ----------
    public boolean isDataFlavorSupported(DataFlavor flavor) {
        return flavor.equals(META_FLAVOR);
    }
    public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
        if(flavor.equals(META_FLAVOR)) {
            return this;
        } else {
            throw new UnsupportedFlavorException(flavor);
        }
    }
    public DataFlavor[] getTransferDataFlavors() {
        return flavors;
    }
    
    // ---------- Sortable Tree Node ----------
    /**
    * Sets the comparator by which this node should sort it's children
    * @param newComparator The comparator to use to sort this node's children
    */
    public void setComparator(Comparator<AccountTreeNode> newComparator) {
        comparator = newComparator;
        sortChildren(children.toArray());
    }
    protected void sortChildren(Object[] changedChildren) {
        int cCount = changedChildren.length;
        if (cCount > 0) {
            int counter;
            for (counter = 0; counter < cCount; counter++) {
                remove((MutableTreeNode) (changedChildren[counter]));
            }
            for (counter = 0; counter < cCount; counter++) {
                add((MutableTreeNode) (changedChildren[counter]));
            }
        }
    }
    @SuppressWarnings("unchecked")
    @Override
    public void add(final MutableTreeNode newChild) {
        if (newChild != null && newChild.getParent() == this) {
            remove(newChild);
        }
        int index;
        if (children == null) {
            index = 0;
        } else {
            index = Collections.binarySearch(children, newChild, (Comparator)comparator);
        }
        if (index < 0) {
            index = -index - 1;
        }
        insert(newChild, index);
    }
    public void treeNodesChanged(TreeModelEvent e) {
        fireTreeNodesChanged(e.getSource(), e.getPath(), e.getChildIndices(), e.getChildren());
        if (e.getTreePath().getLastPathComponent() == this) {
            sortChildren(e.getChildren());
        }
    }
    public void treeStructureChanged(TreeModelEvent e) {
        fireTreeStructureChanged(e.getSource(), e.getPath(), e.getChildIndices(), e.getChildren());
        if (e.getTreePath().getLastPathComponent() == this) {
            sortChildren(children.toArray());
        }
    }
    protected void fireTreeStructureChanged() {
        fireTreeStructureChanged(new int[0], null);
    }
    protected void fireTreeStructureChanged(int[] childIndices,Object[] children) {
        fireTreeStructureChanged(this, getPath(), childIndices, children);
    }
    protected void fireTreeStructureChanged(Object source, Object[] path, int[] childIndices, Object[] children) {
        Object[] listeners = listenerList.getListenerList();
        TreeModelEvent e = null;
        for (int i = listeners.length - 2; i >= 0; i -= 2) {
            if (listeners[i] == TreeModelListener.class) {
                if (e == null) {
                    e = new TreeModelEvent(source, path, childIndices, children);
                }
                ((TreeModelListener) listeners[i + 1]).treeStructureChanged(e);
            }
        }
    }
    protected void fireTreeNodesChanged(int[] childIndices, Object[] children) {
        fireTreeNodesChanged(getPath(), childIndices, children);
    }
    protected void fireTreeNodesChanged(Object[] path, int[] childIndices,Object[] children) {
        fireTreeNodesChanged(this, path, childIndices, children);
    }
    protected void fireTreeNodesChanged(Object source, Object[] path,int[] childIndices, Object[] children) {
        Object[] listeners = listenerList.getListenerList();
        TreeModelEvent e = null;
        for (int i = listeners.length - 2; i >= 0; i -= 2) {
            if (listeners[i] == TreeModelListener.class) {
                if (e == null) {
                    e = new TreeModelEvent(source, path, childIndices, children);
                }
                ((TreeModelListener) listeners[i + 1]).treeNodesChanged(e);
            }
        }
    }
    
    // --------- Serializable --------------
    private void writeObject(java.io.ObjectOutputStream out) throws IOException {
      out.defaultWriteObject();
    }
    private void readObject(java.io.ObjectInputStream in)
      throws IOException, ClassNotFoundException {
      in.defaultReadObject();
    }
}
